/**
  ******************************************************************************
  * @author     Chris
  * @since      2024/6/18 10:40
  *
  * @file       pd_timer_encoder.c
  * @brief      Timer Encoder Interface peripheral driver.
  *
  * @note       This file contains the peripheral driver for the Timer Encoder
  *             Interface.
  *
  * @warning    None.
  ******************************************************************************
  * Change Logs:
  *   Date          Author       Notes
  *   2024/6/18     Chris        the first version
  *
  ******************************************************************************
  */


#include <stm32f4xx_ll_tim.h>
#include <stm32f4xx_ll_gpio.h>
#include "pd_timer_encoder.h"

static void TimerEncoder_init(TimerEncoder *this) {

    // GPIO init
    GPIO **gpio = this->timerCH->gpios;
    for (int i = 0; i < this->timerCH->gpioSize; ++i) {
        gpio[i]->Speed = LL_GPIO_SPEED_FREQ_HIGH;
        gpio[i]->Mode = LL_GPIO_MODE_ALTERNATE;
        gpio[i]->init(gpio[i]);
    }

    // Timer Base Unit
    this->timer->init(this->timer);

    // Timer IC filter and edge polarity
    LL_TIM_IC_InitTypeDef TIM_IC_InitStruct = {0};
    LL_TIM_IC_StructInit(&TIM_IC_InitStruct);
    TIM_IC_InitStruct.ICFilter = 0xF;
    uint32_t *channel = this->timerCH->channels;
    for (int i = 0; i < this->timerCH->chSize; ++i) {
        LL_TIM_IC_Init(this->timer->TIMx, channel[i], &TIM_IC_InitStruct);
    }

    // Encoder Interface
    LL_TIM_ENCODER_InitTypeDef TIM_ENCODER_InitStruct = {0};

    LL_TIM_ENCODER_StructInit(&TIM_ENCODER_InitStruct);
    TIM_ENCODER_InitStruct.EncoderMode    = this->EncoderMode;
    TIM_ENCODER_InitStruct.IC1Polarity    = this->IC1Polarity;
    TIM_ENCODER_InitStruct.IC1ActiveInput = this->IC1ActiveInput;
    TIM_ENCODER_InitStruct.IC1Prescaler   = this->IC1Prescaler;
    TIM_ENCODER_InitStruct.IC1Filter      = this->IC1Filter;
    TIM_ENCODER_InitStruct.IC2Polarity    = this->IC2Polarity;
    TIM_ENCODER_InitStruct.IC2ActiveInput = this->IC2ActiveInput;
    TIM_ENCODER_InitStruct.IC2Prescaler   = this->IC2Prescaler;
    TIM_ENCODER_InitStruct.IC2Filter      = this->IC2Filter;
    LL_TIM_ENCODER_Init(this->timer->TIMx, &TIM_ENCODER_InitStruct);

    LL_TIM_EnableCounter(this->timer->TIMx);

}

static int16_t TimerEncoder_getCounter(TimerEncoder *this) {
    int16_t temp = (int16_t) LL_TIM_GetCounter(this->timer->TIMx);
    LL_TIM_SetCounter(this->timer->TIMx, 0);
    return temp;
}

static TimerEncoder *build(Timer *timer, TimerCh *timerChannel) {
    TimerEncoder *te = malloc(sizeof(TimerEncoder));

    Driver_add(te, GEN_PERM);

    te->timer          = timer;
    te->timerCH        = timerChannel;
    te->EncoderMode    = LL_TIM_ENCODERMODE_X4_TI12;
    te->IC1Polarity    = LL_TIM_IC_POLARITY_RISING;
    te->IC1ActiveInput = LL_TIM_ACTIVEINPUT_DIRECTTI;
    te->IC1Prescaler   = LL_TIM_ICPSC_DIV1;
    te->IC1Filter      = LL_TIM_IC_FILTER_FDIV1;
    te->IC2Polarity    = LL_TIM_IC_POLARITY_RISING;
    te->IC2ActiveInput = LL_TIM_ACTIVEINPUT_DIRECTTI;
    te->IC2Prescaler   = LL_TIM_ICPSC_DIV1;
    te->IC2Filter      = LL_TIM_IC_FILTER_FDIV1;

    te->init           = &TimerEncoder_init;
    te->getCounter     = &TimerEncoder_getCounter;

    return te;
}

const struct TimerEncoderClass TimerEncoderBuilder = {.build = &build};